# Copyright 2011 James McCauley
#
# This file is part of POX.
#
# POX is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# POX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with POX.  If not, see <http://www.gnu.org/licenses/>.

# This file was originally based on pyopenflow.py from NOX, which was
# autogenerated from openflow.h via a program by KK Yap.  It has been
# substantially altered since then.

import struct
import operator
import collections
import sys
from pox.lib.packet.packet_base import packet_base
from pox.lib.packet.ethernet import ethernet
from pox.lib.packet.vlan import vlan
from pox.lib.packet.ipv4 import ipv4
from pox.lib.packet.udp import udp
from pox.lib.packet.tcp import tcp
from pox.lib.packet.icmp import icmp
from pox.lib.packet.arp import arp
from pox.lib.packet.mpls import mpls

from pox.lib.addresses import *
from pox.lib.util import assert_type
from pox.lib.util import initHelper
from pox.lib.util import hexdump

_PAD = b'\x00'
_PAD2 = _PAD*2
_PAD3 = _PAD*3
_PAD4 = _PAD*4
_PAD6 = _PAD*6


EMPTY_ETH = EthAddr(None)

MAX_XID = 0x7fFFffFF
_nextXID = 1
#USE_MPLS_MATCH = False

def generateXID ():
  global _nextXID
  r = _nextXID
  _nextXID += 1
  _nextXID = (_nextXID + 1) % (MAX_XID + 1)
  return r

def xid_generator(start=1):
  """ generate a xid sequence. Wraps at 2**31-1 """
  n = start % (MAX_XID + 1)
  while True:
    yield n
    n  = ( n + 1 )  % (MAX_XID + 1)

def _format_body (body, prefix):
  if hasattr(body, 'show'):
    #TODO: Check this (spacing may well be wrong)
    return body.show(prefix + '  ')
  else:
    return prefix + hexdump(body).replace("\n", "\n" + prefix)

TABLE_ALL = 0xff
TABLE_EMERGENCY = 0xfe

# Structure definitions

#1. Openflow Header
class ofp_header (object):
  def __init__ (self, **kw):
    self.version = OFP_VERSION
    self.header_type = 0
    self.length = 8
    self.xid = None
    initHelper(self, kw)

  def _assert (self):
    if self.header_type not in ofp_type_map:
      return (False, "type is not a known message type")
    return (True, None)

  def pack (self, assertstruct=True):
    if self.xid is None:
      self.xid = generateXID()
    if(assertstruct):
      if(not ofp_header._assert(self)[0]):
        raise RuntimeError("assertstruct failed")
    packed = ""
    packed += struct.pack("!BBHL", self.version, self.header_type, self.length,
                          self.xid)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.version, self.header_type, self.length, self.xid) = struct.unpack_from("!BBHL", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.version !=  other.version: return False
    if self.header_type !=  other.header_type: return False
    if self.length !=  other.length: return False
    if self.xid !=  other.xid: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'version: ' + str(self.version) + '\n'
    outstr += prefix + 'type:    ' + str(self.header_type)# + '\n'
    outstr += " (" + ofp_type_map.get(self.header_type, "Unknown") + ")\n"
    outstr += prefix + 'length:  ' + str(self.length) + '\n'
    outstr += prefix + 'xid:     ' + str(self.xid) + '\n'
    return outstr
  
  def __str__ (self):
    return self.__class__.__name__ + "\n  " + self.show('  ').strip()

#2. Common Structures
##2.1 Port Structures
class ofp_phy_port (object):
  def __init__ (self, **kw):
    self.port_no = 0
    self.hw_addr = EMPTY_ETH
    self.name = ""
    self.config = 0
    self.state = 0
    self.curr = 0
    self.advertised = 0
    self.supported = 0
    self.peer = 0
    initHelper(self, kw)

  def _assert (self):
    if not isinstance(self.hw_addr, bytes) and not isinstance(self.hw_addr, EthAddr):
      return (False, "hw_addr is not bytes or EthAddr")
    if(len(self.hw_addr) != 6):
      return (False, "hw_addr is not of size 6")
    if(not isinstance(self.name, str)):
      return (False, "name is not string")
    if(len(self.name) > 16):
      return (False, "name is not of size 16")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!H", self.port_no)
    packed += self.hw_addr if isinstance(self.hw_addr, bytes) else self.hw_addr.toRaw()
    packed += self.name.ljust(16,'\0')
    packed += struct.pack("!LLLLLL", self.config, self.state, self.curr, self.advertised, self.supported, self.peer)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 48):
      return binaryString
    (self.port_no,) = struct.unpack_from("!H", binaryString, 0)
    self.hw_addr = EthAddr(binaryString[2:8])
    self.name = binaryString[8:24].replace("\0","")
    (self.config, self.state, self.curr, self.advertised, self.supported, self.peer) = struct.unpack_from("!LLLLLL", binaryString, 24)
    return binaryString[48:]

  def __len__ (self):
    return 48

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.port_no !=  other.port_no: return False
    if self.hw_addr !=  other.hw_addr: return False
    if self.name !=  other.name: return False
    if self.config !=  other.config: return False
    if self.state !=  other.state: return False
    if self.curr !=  other.curr: return False
    if self.advertised !=  other.advertised: return False
    if self.supported !=  other.supported: return False
    if self.peer !=  other.peer: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)
  
  def __hash__(self, *args, **kwargs):
    return self.port_no.__hash__() + self.hw_addr.toInt().__hash__() + \
           self.name.__hash__() + self.config.__hash__() + \
           self.state.__hash__() + self.curr.__hash__() + \
           self.advertised.__hash__() + self.supported.__hash__() + \
           self.peer.__hash__()

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    outstr += prefix + 'hw_addr: ' + str(EthAddr(self.hw_addr)) + '\n'
    outstr += prefix + 'name: ' + str(self.name) + '\n'
    outstr += prefix + 'config: ' + str(self.config) + '\n'
    outstr += prefix + 'state: ' + str(self.state) + '\n'
    outstr += prefix + 'curr: ' + str(self.curr) + '\n'
    outstr += prefix + 'advertised: ' + str(self.advertised) + '\n'
    outstr += prefix + 'supported: ' + str(self.supported) + '\n'
    outstr += prefix + 'peer: ' + str(self.peer) + '\n'
    return outstr

  def __repr__(self):
    return self.show()

ofp_port_config_rev_map = {
  'OFPPC_PORT_DOWN'    : 1,
  'OFPPC_NO_STP'       : 2,
  'OFPPC_NO_RECV'      : 4,
  'OFPPC_NO_RECV_STP'  : 8,
  'OFPPC_NO_FLOOD'     : 16,
  'OFPPC_NO_FWD'       : 32,
  'OFPPC_NO_PACKET_IN' : 64,
}

ofp_port_state_rev_map = {
  'OFPPS_STP_LISTEN'  : 0,
  'OFPPS_LINK_DOWN'   : 1,
  'OFPPS_STP_LEARN'   : 256,
  'OFPPS_STP_FORWARD' : 512,
  'OFPPS_STP_BLOCK'   : 768,
}
OFPPS_STP_MASK        = 768

ofp_port_features_rev_map = {
  'OFPPF_10MB_HD'    : 1,
  'OFPPF_10MB_FD'    : 2,
  'OFPPF_100MB_HD'   : 4,
  'OFPPF_100MB_FD'   : 8,
  'OFPPF_1GB_HD'     : 16,
  'OFPPF_1GB_FD'     : 32,
  'OFPPF_10GB_FD'    : 64,
  'OFPPF_COPPER'     : 128,
  'OFPPF_FIBER'      : 256,
  'OFPPF_AUTONEG'    : 512,
  'OFPPF_PAUSE'      : 1024,
  'OFPPF_PAUSE_ASYM' : 2048,
}

##2.2 Queue Structures
class ofp_packet_queue (object):
  def __init__ (self, **kw):
    self.queue_id = 0
    self.length = 0
    self.properties = []

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!LH", self.queue_id, self.length)
    packed += _PAD2 # Pad
    for i in self.properties:
      packed += i.pack(assertstruct)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.queue_id, self.length) = struct.unpack_from("!LH", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    l = 8
    for i in self.properties:
      l += len(i)
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.queue_id !=  other.queue_id: return False
    if self.length !=  other.length: return False
    if self.properties !=  other.properties: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'properties: \n'
    for obj in self.properties:
      outstr += obj.show(prefix + '  ')
    return outstr

ofp_queue_properties_rev_map = {
  'OFPQT_MIN_RATE' : 0,
}
OFPQT_NONE         = 0

class ofp_queue_prop_header (object):
  def __init__ (self, **kw):
    self.property = 0
    self.length = 8

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HH", self.property, self.length)
    packed += _PAD4 # Pad
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.property, self.length) = struct.unpack_from("!HH", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.property !=  other.property: return False
    if self.length !=  other.length: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'property: ' + str(self.property) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    return outstr

class ofp_queue_prop_min_rate (object):
  def __init__ (self, **kw):
    self.prop_header = ofp_queue_prop_header()
    self.rate = 0

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.prop_header, ofp_queue_prop_header)):
      return (False, "prop_header is not class ofp_queue_prop_header")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += self.prop_header.pack()
    packed += struct.pack("!H", self.rate)
    packed += _PAD6
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 16):
      return binaryString
    self.prop_header.unpack(binaryString[0:])
    (self.rate,) = struct.unpack_from("!H", binaryString, 8)
    return binaryString[16:]

  def __len__ (self):
    return 16

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.prop_header !=  other.prop_header: return False
    if self.rate !=  other.rate: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'prop_header: \n'
    self.prop_header.show(prefix + '  ')
    outstr += prefix + 'rate: ' + str(self.rate) + '\n'
    return outstr

##2.3 Flow Match Structures
class ofp_match (object):
  @classmethod
  def from_packet (cls, packet, in_port = None):
    """ get a match that matches this packet, asuming it came in on in_port in_port
    @param packet an instance of 'ethernet'
    """
    assert_type("packet", packet, ethernet, none_ok=False)

    match = cls()

    if in_port is not None:
      match.in_port = in_port

    match.dl_src = packet.src
    match.dl_dst = packet.dst
    match.dl_type = packet.type
    p = packet.next
#    if isinstance(p, mpls):
#      match.mpls_label = p.label
#      match.mpls_tc = p.tc
#    else:
#      match.mpls_label = 0
#      match.mpls_tc = 0
    if isinstance(p, vlan):
      match.dl_type = p.eth_type
      match.dl_vlan = p.id
      match.dl_vlan_pcp = p.pcp
      p = p.next
    else:
      match.dl_vlan = OFP_VLAN_NONE
      match.dl_vlan_pcp = 0

    if isinstance(p, ipv4):
      match.nw_src = p.srcip
      match.nw_dst = p.dstip
      match.nw_proto = p.protocol
      match.nw_tos = p.tos
      p = p.next

      if isinstance(p, udp) or isinstance(p, tcp):
        match.tp_src = p.srcport
        match.tp_dst = p.dstport
      elif isinstance(p, icmp):
        match.tp_src = p.type
        match.tp_dst = p.code
    elif isinstance(p, arp):
      if p.opcode <= 255:
        match.nw_proto = p.opcode
        match.nw_src = p.protosrc
        match.nw_dst = p.protodst

    return match

  def optimize (self):
    """
    Reduce the number of wildcards used.
    """
    #TODO: Fix for optional cases (i.e. ARP)
    if self.dl_vlan == OFP_VLAN_NONE:
      self.dl_vlan_pcp = 0

    #TODO: What do we do when something is "behind" a wildcard?
    #      e.g., does nw_src count if dl_type is wild or only if it's 0x0800?
    if self.dl_type is not None:
      if self.dl_type != 0x0800:
        # Not IP
        if self.dl_type != 0x0806:
          # Not IP or ARP
          self.nw_src = IPAddr(0)
          self.nw_dst = IPAddr(0)
          eelf.nw_proto = 0
        self.nw_tos = 0
        self.tp_src = 0
        self.tp_dst = 0
      else:
        # It's IP
        if self.nw_proto != 6 and self.nw_proto != 17 and self.nw_proto != 1:
          # Not TCP, UDP, or ICMP
          self.tp_src = 0
          self.tp_dst = 0
    self.wildcards = self._normalize_wildcards(self.wildcards)
    return self # for chaining

  def clone (self):
    n = ofp_match()
    for k,v in ofp_match_data.iteritems():
      setattr(n, '_' + k, getattr(self, '_' + k))
    n.wildcards = self.wildcards
    return n

  def __init__ (self, **kw):
    for k,v in ofp_match_data.iteritems():
      setattr(self, '_' + k, v[0])

    self.wildcards = self._normalize_wildcards(OFPFW_ALL)

    # This is basically initHelper(), but tweaked slightly since this
    # class does some magic of its own.
    for k,v in kw.iteritems():
      if not hasattr(self, '_'+k):
        raise TypeError(self.__class__.__name__ + " constructor got "
          + "unexpected keyword argument '" + k + "'")
      setattr(self, k, v)

  def get_nw_dst (self):
    if (self.wildcards & OFPFW_NW_DST_ALL) == OFPFW_NW_DST_ALL: return (None, 0)

    w = (self.wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT
    return (self._nw_dst,32-w if w <= 32 else 0)

  def get_nw_src (self):
    if (self.wildcards & OFPFW_NW_SRC_ALL) == OFPFW_NW_SRC_ALL: return (None, 0)

    w = (self.wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT
    return (self._nw_src,32-w if w <= 32 else 0)

  def set_nw_dst (self, *args, **kw):
    a = self._make_addr(*args, **kw)
    if a == None:
      self._nw_src = ofp_match_data['nw_dst'][0]
      self.wildcards &= ~OFPFW_NW_DST_MASK
      self.wildcards |= ofp_match_data['nw_dst'][1]
      return
    self._nw_dst = a[0]
    self.wildcards &= ~OFPFW_NW_DST_MASK
    self.wildcards |= ((32-a[1]) << OFPFW_NW_DST_SHIFT)

  def set_nw_src (self, *args, **kw):
    a = self._make_addr(*args, **kw)
      # self.internal_links.add(Link(edge, edge.ports[port_no], host, host.interfaces[0]))
    if a == None:
      self._nw_src = ofp_match_data['nw_src'][0]
      self.wildcards &= ~OFPFW_NW_SRC_MASK
      self.wildcards |= ofp_match_data['nw_src'][1]
      return
    self._nw_src = a[0]
    self.wildcards &= ~OFPFW_NW_SRC_MASK
    self.wildcards |= ((32-a[1]) << OFPFW_NW_SRC_SHIFT)

  def _make_addr (self, ipOrIPAndBits, bits=None):
    if ipOrIPAndBits == None: return None
    b = None
    if type(ipOrIPAndBits) is tuple:
      ip = ipOrIPAndBits[0]
      b = int(ipOrIPAndBits[1])

    if (type(ipOrIPAndBits) is str) and (len(ipOrIPAndBits) != 4):
      if ipOrIPAndBits.find('/') != -1:
        s = ipOrIPAndBits.split('/')
        ip = s[0]
        b = int(s[1]) if b is None else b
      else:
        ip = ipOrIPAndBits
        b = 32 if b is None else b
    else:
      ip = ipOrIPAndBits
      b = 32 if b is None else b

    if type(ip) is str:
      ip = IPAddr(ip)

    if bits != None: b = bits
    if b > 32: b = 32
    elif b < 0: b = 0

    return (ip, b)

  def __setattr__ (self, name, value):
    if name not in ofp_match_data:
      self.__dict__[name] = value
      return

    if name == 'nw_dst' or name == 'nw_src':
      # Special handling
      getattr(self, 'set_' + name)(value)
      return value

    if value is None:
      setattr(self, '_' + name, ofp_match_data[name][0])
      self.wildcards |= ofp_match_data[name][1]
    else:
      setattr(self, '_' + name, value)
      self.wildcards = self.wildcards & ~ofp_match_data[name][1]

    return value

  def __getattr__ (self, name):
    if name in ofp_match_data:
      if (self.wildcards & ofp_match_data[name][1]) == ofp_match_data[name][1]:
        # It's wildcarded -- always return None
        return None
      if name == 'nw_dst' or name == 'nw_src':
        # Special handling
        return getattr(self, 'get_' + name)()[0]
      return self.__dict__['_' + name]
    raise AttributeError("attribute not found: "+name)

  def _assert (self):
    #if not isinstance(self._dl_src, list):
    #  return "self.dl_src is not list"
    #if len(self._dl_src) != 6:
    #  return "self.dl_src is not of size 6"
    #if not isinstance(self._dl_dst, list):
    #  return "self.dl_dst is not list"
    if len(self._dl_dst) != 6:
      return "self.dl_dst is not of size 6"
    return None

  def pack (self, assertstruct=True, flow_mod=False):
    if(assertstruct):
      if self._assert() is not None:
        raise RuntimeError(self._assert())

    packed = ""
    packed += struct.pack("!LH", self._wire_wildcards(self.wildcards) if flow_mod else self.wildcards, self.in_port or 0)
    if self.dl_src == None:
      packed += EMPTY_ETH.toRaw()
    elif type(self.dl_src) is bytes:
      packed += self.dl_src
    else:
      packed += self.dl_src.toRaw()
    if self.dl_dst == None:
      packed += EMPTY_ETH.toRaw()
    elif type(self.dl_dst) is bytes:
      packed += self.dl_dst
    else:
      packed += self.dl_dst.toRaw()

    def check_ip(val):
      return (val or 0) if self.dl_type == 0x0800 else 0
    def check_ip_or_arp(val):
      return (val or 0) if self.dl_type == 0x0800 or self.dl_type == 0x0806 else 0
    def check_tp(val):
      return (val or 0) if self.dl_type == 0x0800 and self.nw_proto in (1,6,17) else 0

    packed += struct.pack("!HB", self.dl_vlan or 0, self.dl_vlan_pcp or 0)
    packed += _PAD # Hardcode padding
    packed += struct.pack("!HBB", self.dl_type or 0, check_ip(self.nw_tos), check_ip_or_arp(self.nw_proto))
    packed += _PAD2 # Hardcode padding
    def fix (addr):
      if addr is None: return 0
      if type(addr) is int: return addr & 0xffFFffFF
      if type(addr) is long: return addr & 0xffFFffFF
      return addr.toUnsigned()
    packed += struct.pack("!LLHH", check_ip_or_arp(fix(self.nw_src)), check_ip_or_arp(fix(self.nw_dst)),
                          check_tp(self.tp_src), check_tp(self.tp_dst))
#    if USE_MPLS_MATCH:
#        packed += struct.pack("!IBxxx", self.mpls_label or 0, self.mpls_tc or 0)
    return packed

  def _normalize_wildcards (self, wildcards):
    """ nw_src and nw_dst values greater than 32 mean the same thing as 32.
      We normalize them here just to be clean and so that comparisons act
      as you'd want them to. """
    if ((wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT) > 32:
      wildcards &= ~OFPFW_NW_SRC_MASK
      wildcards |= (32 << OFPFW_NW_SRC_SHIFT)
    if ((wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT) > 32:
      wildcards &= ~OFPFW_NW_DST_MASK
      wildcards |= (32 << OFPFW_NW_DST_SHIFT)
    return wildcards

  def _wire_wildcards(self, wildcards):
    """ Normallize the wildcard bits to the openflow wire representation. Note this
        atrocity from the OF1.1 spec:
        Protocol-specific fields within ofp_match will be ignored within
        a single table when the corresponding protocol is not specified in the
        match. The MPLS match fields will be ignored unless the Ethertype is
        specified as MPLS. Likewise, the IP header and transport header fields
        will be ignored unless the Ethertype is specified as either IPv4 or
        ARP. The tp_src and tp_dst fields will be ignored unless the network
        protocol specified is as TCP, UDP or SCTP. Fields that are ignored
        don't need to be wildcarded and should be set to 0.
    """
    if self.dl_type == 0x0800:
        # IP
        if  self.nw_proto not in (1,6,17):
          # not TCP/UDP/ICMP -> Clear TP wildcards for the wire
          return wildcards & ~(OFPFW_TP_SRC | OFPFW_TP_DST)
        else:
          return wildcards
    elif self.dl_type == 0x0806:
        # ARP: clear NW_TOS / TP wildcards for the wire
        return wildcards & ~( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST)
    else:
        # not even IP. Clear NW/TP wildcards for the wire
        return wildcards & ~( OFPFW_NW_TOS | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_TP_SRC | OFPFW_TP_DST)


  def _unwire_wildcards(self, wildcards):
    """ Normallize the wildcard bits from the openflow wire representation. Note this
        atrocity from the OF1.1 spec:
        Protocol-specific fields within ofp_match will be ignored within
        a single table when the corresponding protocol is not specified in the
        match. The MPLS match fields will be ignored unless the Ethertype is
        specified as MPLS. Likewise, the IP header and transport header fields
        will be ignored unless the Ethertype is specified as either IPv4 or
        ARP. The tp_src and tp_dst fields will be ignored unless the network
        protocol specified is as TCP, UDP or SCTP. Fields that are ignored
        don't need to be wildcarded and should be set to 0.
    """
    if self._dl_type == 0x0800:
        # IP
        if  self._nw_proto not in (1,6,17):
          # not TCP/UDP/ICMP -> Set TP wildcards for the object
          return wildcards | (OFPFW_TP_SRC | OFPFW_TP_DST)
        else:
          return wildcards
    elif self._dl_type == 0x0806:
        # ARP: Set NW_TOS / TP wildcards for the object
        return wildcards  | ( OFPFW_NW_TOS | OFPFW_TP_SRC | OFPFW_TP_DST)
    else:
        # not even IP. Set NW/TP wildcards for the object
        return wildcards  | ( OFPFW_NW_TOS | OFPFW_NW_PROTO | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_TP_SRC | OFPFW_TP_DST)


  @property
  def is_wildcarded (self):
    return self.wildcards & OFPFW_ALL != 0

  @property
  def is_exact (self):
    return not self.is_wildcarded

  def unpack (self, binaryString, flow_mod=False):
    if (len(binaryString) < self.__len__()):
      return binaryString
    (wildcards, self._in_port) = struct.unpack_from("!LH", binaryString, 0)
    self._dl_src = EthAddr(struct.unpack_from("!BBBBBB", binaryString, 6))
    self._dl_dst = EthAddr(struct.unpack_from("!BBBBBB", binaryString, 12))
    (self._dl_vlan, self._dl_vlan_pcp) = struct.unpack_from("!HB", binaryString, 18)
    (self._dl_type, self._nw_tos, self._nw_proto) = struct.unpack_from("!HBB", binaryString, 22)
    (self._nw_src, self._nw_dst, self._tp_src, self._tp_dst) = struct.unpack_from("!LLHH", binaryString, 28)
    self._nw_src = IPAddr(self._nw_src)
    self._nw_dst = IPAddr(self._nw_dst)
#    if USE_MPLS_MATCH:
#      (self.mpls_label, self.mpls_tc) = struct.unpack_from("!IBxxx", binaryString, 40)
    self.wildcards = self._normalize_wildcards(self._unwire_wildcards(wildcards) if flow_mod else wildcards) # Overide
    return binaryString[self.__len__():]

  def __len__ (self):
 #   if USE_MPLS_MATCH:
 #     return 48
    return 40

  def hash_code (self):
    '''
    ofp_match is not properly hashable since it is mutable, but it can still be
    useful to easily generate a hash code.
    '''

    h = self.wildcards
    for f in ofp_match_data:
      v = getattr(self, f)
      if type(v) is int:
        h ^= v
      elif type(v) is long:
        h ^= v

    return int(h & 0x7fFFffFF)

  def matches_with_wildcards (self, other, consider_other_wildcards=True):
    """
    Test whether /this/ match completely encompasses the other match. Important for non-strict modify flow_mods etc.
    """
    assert_type("other", other, ofp_match, none_ok=False)
    # short cut for equal matches
    if(self == other): return True
    # only candidate if all wildcard bits in the *other* match are also set in this match (i.e., a submatch)

    # first compare the bitmask part
    if(consider_other_wildcards):
      self_bits  = self.wildcards & ~(OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK)
      other_bits = other.wildcards & ~(OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK)
      if( self_bits | other_bits != self_bits): return False

    def match_fail(mine, others):
      return mine != None and mine != others

    if match_fail(self.in_port, other.in_port): return False
    if match_fail(self.dl_vlan, other.dl_vlan): return False
    if match_fail(self.dl_src, other.dl_src): return False
    if match_fail(self.dl_dst, other.dl_dst): return False
    if match_fail(self.dl_type, other.dl_type): return False
    if match_fail(self.nw_proto, other.nw_proto): return False
    if match_fail(self.tp_src, other.tp_src): return False
    if match_fail(self.tp_dst, other.tp_dst): return False
    if match_fail(self.dl_vlan_pcp, other.dl_vlan_pcp): return False
    if match_fail(self.nw_tos, other.nw_tos): return False

    self_nw_src = self.get_nw_src()
    if(self_nw_src[0] != None):
      other_nw_src = other.get_nw_src()
      if self_nw_src[1] > other_nw_src[1] or not IPAddr(other_nw_src[0]).inNetwork((self_nw_src[0], 32-self_nw_src[1])): return False

    self_nw_dst = self.get_nw_dst()
    if(self_nw_dst[0] != None):
      other_nw_dst = other.get_nw_dst()
      if self_nw_dst[1] > other_nw_dst[1] or not IPAddr(other_nw_dst[0]).inNetwork((self_nw_dst[0], 32-self_nw_dst[1])): return False
    return True

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.wildcards !=  other.wildcards: return False
    if self.in_port !=  other.in_port: return False
    if self.dl_src !=  other.dl_src: return False
    if self.dl_dst !=  other.dl_dst: return False
    if self.dl_vlan !=  other.dl_vlan: return False
    if self.dl_vlan_pcp !=  other.dl_vlan_pcp: return False
    if self.dl_type !=  other.dl_type: return False
    if self.nw_tos !=  other.nw_tos: return False
    if self.nw_proto !=  other.nw_proto: return False
    if self.nw_src !=  other.nw_src: return False
    if self.nw_dst !=  other.nw_dst: return False
    if self.tp_src !=  other.tp_src: return False
    if self.tp_dst !=  other.tp_dst: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def __str__ (self):
    return self.__class__.__name__ + "\n  " + self.show('  ').strip()

  def show (self, prefix=''):
    def binstr (n):
      s = ''
      while True:
        s = ('1' if n & 1 else '0') + s
        n >>= 1
        if n == 0: break
      return s
    def safehex(n):
      if n == None:
        return "(None)"
      else:
        return hex(n)

    def show_wildcards(w):
      parts = [ k.lower()[len("OFPFW_"):] for (k,v) in ofp_flow_wildcards_rev_map.iteritems() if v & w == v ]
      nw_src_bits = (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT
      if(nw_src_bits > 0): parts.append("nw_src(/%d)" % (32 - nw_src_bits))

      nw_dst_bits = (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT
      if(nw_dst_bits > 0): parts.append("nw_dst(/%d)" % (32 - nw_dst_bits))
      return "|".join(parts)

    outstr = ''
    outstr += prefix + 'wildcards: ' + show_wildcards(self.wildcards) + ' (' + binstr(self.wildcards) + ' = ' + hex(self.wildcards) + ')\n'
    def append (f, formatter=str):
      v = self.__getattr__(f)
      if v is None: return ''
      return prefix + f + ": " + formatter(v) + "\n"
    outstr += append('in_port')
    outstr += append('dl_src')
    outstr += append('dl_dst')
    outstr += append('dl_vlan')
    outstr += append('dl_vlan_pcp')
    outstr += append('dl_type', safehex)
    outstr += append('nw_tos')
    outstr += append('nw_proto')
    outstr += append('nw_src')
    outstr += append('nw_dst')
    outstr += append('tp_src')
    outstr += append('tp_dst')
#    outstr += append('mpls_label')
#    outstr += append('mpls_tc')
    return outstr

ofp_flow_wildcards_rev_map = {
  'OFPFW_IN_PORT'      : 1,
  'OFPFW_DL_VLAN'      : 2,
  'OFPFW_DL_SRC'       : 4,
  'OFPFW_DL_DST'       : 8,
  'OFPFW_DL_TYPE'      : 16,
  'OFPFW_NW_PROTO'     : 32,
  'OFPFW_TP_SRC'       : 64,
  'OFPFW_TP_DST'       : 128,
#  'OFPFW_MPLS_LABEL'   : 1 << 21,
#  'OFPFW_MPLS_TC'      : 1 << 22,
  'OFPFW_DL_VLAN_PCP'  : 1048576,
  'OFPFW_NW_TOS'       : 1<<21,
}

OFPFW_NW_DST_BITS      = 6
OFPFW_NW_SRC_BITS      = 6
OFPFW_NW_SRC_SHIFT     = 8
OFPFW_NW_DST_SHIFT     = 14
OFPFW_NW_SRC_ALL       = 8192
OFPFW_NW_SRC_MASK      = 16128
OFPFW_NW_DST_ALL       = 524288
OFPFW_NW_DST_MASK      = 1032192
# Note: Need to handle all flags that are set in this
# glob-all masks in the packet handling methods. (Esp. ofp_match.from_packet)
# Otherwise, packets are not being matched as they should
OFPFW_ALL              = ((1 << 22) - 1)

##2.4 Flow Action Structures
ofp_action_type_rev_map = {
  'OFPAT_OUTPUT'       : 0,
  'OFPAT_SET_VLAN_VID' : 1,
  'OFPAT_SET_VLAN_PCP' : 2,
  'OFPAT_STRIP_VLAN'   : 3,
  'OFPAT_SET_DL_SRC'   : 4,
  'OFPAT_SET_DL_DST'   : 5,
  'OFPAT_SET_NW_SRC'   : 6,
  'OFPAT_SET_NW_DST'   : 7,
  'OFPAT_SET_NW_TOS'   : 8,
  'OFPAT_SET_TP_SRC'   : 9,
  'OFPAT_SET_TP_DST'   : 10,
  'OFPAT_ENQUEUE'      : 11,
  'OFPAT_SET_MPLS_LABEL':13,
  'OFPAT_SET_MPLS_TC'  : 14,
  'OFPAT_SET_MPLS_TTL' : 15,
  'OFPAT_DEC_MPLS_TTL' : 16,
  'OFPAT_PUSH_MPLS'    : 19,
  'OFPAT_POP_MPLS'     : 20,
  'OFPAT_RESUBMIT'     : 21,
  'OFPAT_VENDOR'       : 65535,
}

class ofp_action_header (object):
  def __init__ (self, **kw):
    self.type = None # Purposely bad
    self.length = 8
    self.data = b''

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HH", self.type, self.length)
    packed += _PAD4 # Pad
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length) = struct.unpack_from("!HH", binaryString, 0)
    if len(binaryString) < self.length: return binaryString
    self.data = binaryString[8:8+self.length]
    return binaryString[self.length:]

  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    return outstr

class ofp_action_output (object):
  def __init__ (self, **kw):
    self.type = OFPAT_OUTPUT
    self.length = 8
    self.port = None # Purposely bad -- require specification
    self.max_len = 0xffFF

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if self.port != OFPP_CONTROLLER:
      self.max_len = 0
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHHH", self.type, self.length, self.port, self.max_len)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.port, self.max_len) = struct.unpack_from("!HHHH", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.port !=  other.port: return False
    if self.max_len !=  other.max_len: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'port: ' + str(self.port) + '\n'
    outstr += prefix + 'max_len: ' + str(self.max_len) + '\n'
    return outstr

class ofp_action_enqueue (object):
  def __init__ (self, **kw):
    self.type = OFPAT_ENQUEUE
    self.length = 16
    self.port = 0
    self.queue_id = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHH", self.type, self.length, self.port)
    packed += _PAD6 # Pad
    packed += struct.pack("!L", self.queue_id)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 16):
      return binaryString
    (self.type, self.length, self.port) = struct.unpack_from("!HHH", binaryString, 0)
    (self.queue_id,) = struct.unpack_from("!L", binaryString, 12)
    return binaryString[16:]

  def __len__ (self):
    return 16

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.port !=  other.port: return False
    if self.queue_id !=  other.queue_id: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'port: ' + str(self.port) + '\n'
    outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
    return outstr

class ofp_action_push_mpls (object):
  """ For now a push mpls action, but we can use this for
    push vlan too some day"""
  unicast_mpls_ethertype = 0x8847
  multicast_mpls_ethertype = 0x8848
  def __init__ (self, **kw):
    self.type = OFPAT_PUSH_MPLS
    self.length = 8
    self.ethertype = ofp_action_push_mpls.unicast_mpls_ethertype

    initHelper(self, kw)
  
  def _assert(self):
    return ((self.ethertype == ofp_action_push_mpls.unicast_mpls_ethertype or 
            self.ethertype == ofp_action_push_mpls.multicast_mpls_ethertype), 
            None)
  
  def pack (self, assertstruct = True):
    if (assertstruct):
      if not (self._assert()[0]):
        return None
      packed = ""
      packed += struct.pack("!HHHxx", self.type, self.length, self.ethertype)
      return packed
  
  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.ethertype) = struct.unpack_from("!HHH", binaryString, 0)
    return binaryString[8:]
 
  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type != other.type: return False
    if self.length != other.length: return False
    if self.ethertype != other.ethertype: return False
    return True
  
  def __ne__ (self, other): 
    return not self.__eq__(other)

  def show (self, prefix = ''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'ethertype: ' + str(self.ethertype) + '\n'
    return outstr

class ofp_action_mpls_label (object):
  def __init__ (self, **kw):
    self.type = OFPAT_SET_MPLS_LABEL
    self.length = 8
    self.mpls_label = 0

    initHelper(self, kw)
  
  def _assert(self):
    return (True, None)
  
  def pack (self, assertstruct = True):
    if (assertstruct):
      if not (self._assert()[0]):
        return None
      packed = ""
      packed += struct.pack("!HHI", self.type, self.length, self.mpls_label)
      return packed
  
  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.mpls_label) = struct.unpack_from("!HHI", binaryString, 0)
    return binaryString[8:]
 
  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type != other.type: return False
    if self.length != other.length: return False
    if self.mpls_label != other.mpls_label: return False
    return True
  
  def __ne__ (self, other): 
    return not self.__eq__(other)

  def show (self, prefix = ''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'label: ' + str(self.mpls_label) + '\n'
    return outstr

class ofp_action_mpls_tc (object):
  def __init__ (self, **kw):
    self.type = OFPAT_SET_MPLS_TC
    self.length = 8
    self.mpls_tc = 0

    initHelper(self, kw)
  
  def _assert(self):
    return (True, None)
  
  def pack (self, assertstruct = True):
    if (assertstruct):
      if not (self._assert()[0]):
        return None
      packed = ""
      packed += struct.pack("!HHBxxx", self.type, self.length, self.mpls_tc)
      return packed
  
  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.mpls_tc) = struct.unpack_from("!HHB", binaryString, 0)
    return binaryString[8:]
 
  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type != other.type: return False
    if self.length != other.length: return False
    if self.mpls_tc != other.mpls_tc: return False
    return True
  
  def __ne__ (self, other): 
    return not self.__eq__(other)

  def show (self, prefix = ''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'tc: ' + str(self.mpls_tc) + '\n'
    return outstr

class ofp_action_mpls_ttl (object):
  def __init__ (self, **kw):
    self.type = OFPAT_SET_MPLS_TTL
    self.length = 8
    self.mpls_ttl = 0

    initHelper(self, kw)
  
  def _assert(self):
    return (True, None)
  
  def pack (self, assertstruct = True):
    if (assertstruct):
      if not (self._assert()[0]):
        return None
      packed = ""
      packed += struct.pack("!HHBxxx", self.type, self.length, self.mpls_ttl)
      return packed
  
  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.mpls_ttl) = struct.unpack_from("!HHB", binaryString, 0)
    return binaryString[8:]
 
  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type != other.type: return False
    if self.length != other.length: return False
    if self.mpls_ttl != other.mpls_ttl: return False
    return True
  
  def __ne__ (self, other): 
    return not self.__eq__(other)

  def show (self, prefix = ''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'ttl: ' + str(self.mpls_ttl) + '\n'
    return outstr

class ofp_action_mpls_dec_ttl (ofp_action_header):
  def __init__ (self, **kw):
    super(ofp_action_mpls_dec_ttl, self).__init__(**kw)
    self.type = OFPAT_DEC_MPLS_TTL

class ofp_action_resubmit (ofp_action_header):
  def __init__ (self, **kw):
    super(ofp_action_resubmit, self).__init__(**kw)
    self.type = OFPAT_RESUBMIT

class ofp_action_pop_mpls (object):
  def __init__ (self, **kw):
    self.type = OFPAT_POP_MPLS
    self.length = 8
    self.ethertype = 0

    initHelper(self, kw)
  
  def _assert(self):
    return (True, None)
  
  def pack (self, assertstruct = True):
    if (assertstruct):
      if not (self._assert()[0]):
        return None
      packed = ""
      packed += struct.pack("!HHHxx", self.type, self.length, self.ethertype)
      return packed
  
  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.ethertype) = struct.unpack_from("!HHH", binaryString, 0)
    return binaryString[8:]
 
  def __len__ (self):
    return self.length

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type != other.type: return False
    if self.length != other.length: return False
    if self.ethertype != other.ethertype: return False
    return True
  
  def __ne__ (self, other): 
    return not self.__eq__(other)

  def show (self, prefix = ''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'ethertype: ' + str(self.ethertype) + '\n'
    return outstr

class ofp_action_vlan_vid (object):
  def __init__ (self, **kw):
    self.type = OFPAT_SET_VLAN_VID
    self.length = 8
    self.vlan_vid = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHH", self.type, self.length, self.vlan_vid)
    packed += _PAD2 # Pad
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.vlan_vid) = struct.unpack_from("!HHH", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.vlan_vid !=  other.vlan_vid: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'vlan_vid: ' + str(self.vlan_vid) + '\n'
    return outstr

class ofp_action_vlan_pcp (object):
  def __init__ (self, **kw):
    self.type = OFPAT_SET_VLAN_PCP
    self.length = 8
    self.vlan_pcp = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHB", self.type, self.length, self.vlan_pcp)
    packed += _PAD3 # Pad
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.vlan_pcp) = struct.unpack_from("!HHB", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.vlan_pcp !=  other.vlan_pcp: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'vlan_pcp: ' + str(self.vlan_pcp) + '\n'
    return outstr

class ofp_action_dl_addr (object):
  @classmethod
  def set_dst (cls, dl_addr = None):
    return cls(OFPAT_SET_DL_DST, dl_addr)
  @classmethod
  def set_src (cls, dl_addr = None):
    return cls(OFPAT_SET_DL_SRC, dl_addr)

  def __init__ (self, type = None, dl_addr = None):
    """
    'type' should be OFPAT_SET_DL_SRC or OFPAT_SET_DL_DST.
    """
    self.type = type
    self.length = 16
    self.dl_addr = EMPTY_ETH

    if dl_addr is not None:
      self.dl_addr = EthAddr(dl_addr)

  def _assert (self):
    if not isinstance(self.dl_addr, EthAddr) and not isinstance(self.dl_addr, bytes):
      return (False, "dl_addr is not string or EthAddr")
    if isinstance(self.dl_addr, bytes) and len(self.dl_addr) != 6:
      return (False, "dl_addr is not of size 6")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HH", self.type, self.length)
    if isinstance(self.dl_addr, EthAddr):
      packed += self.dl_addr.toRaw()
    else:
      packed += self.dl_addr
    packed += _PAD6
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 16):
      return binaryString
    (self.type, self.length) = struct.unpack_from("!HH", binaryString, 0)
    self.dl_addr = EthAddr(struct.unpack_from("!BBBBBB", binaryString, 4))
    return binaryString[16:]

  def __len__ (self):
    return 16

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.dl_addr !=  other.dl_addr: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'dl_addr: ' + str(self.dl_addr) + '\n'
    return outstr

class ofp_action_nw_addr (object):
  @classmethod
  def set_dst (cls, nw_addr = None):
    return cls(OFPAT_SET_NW_DST, nw_addr)
  @classmethod
  def set_src (cls, nw_addr = None):
    return cls(OFPAT_SET_NW_SRC, nw_addr)

  def __init__ (self, type = None, nw_addr = None):
    """
    'type' should be OFPAT_SET_NW_SRC or OFPAT_SET_NW_DST
    """
    self.type = type
    self.length = 8

    if nw_addr is not None:
      self.nw_addr = IPAddr(nw_addr)
    else:
      self.nw_addr = IPAddr(0)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHl", self.type, self.length, self.nw_addr.toSigned())
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.nw_addr) = struct.unpack_from("!HHL", binaryString, 0)
    self.nw_addr = IPAddr(self.nw_addr, networkOrder=False)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.nw_addr !=  other.nw_addr: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'nw_addr: ' + str(self.nw_addr) + '\n'
    return outstr

class ofp_action_nw_tos (object):
  def __init__ (self, nw_tos = 0):
    self.type = OFPAT_SET_NW_TOS
    self.length = 8
    self.nw_tos = nw_tos

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHB", self.type, self.length, self.nw_tos)
    packed += _PAD3
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.nw_tos) = struct.unpack_from("!HHB", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.nw_tos !=  other.nw_tos: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'nw_tos: ' + str(self.nw_tos) + '\n'
    return outstr

class ofp_action_tp_port (object):
  @classmethod
  def set_dst (cls, tp_port = None):
    return cls(OFPAT_SET_TP_DST, tp_port)
  @classmethod
  def set_src (cls, tp_port = None):
    return cls(OFPAT_SET_TP_SRC, tp_port)

  def __init__ (self, type=None, tp_port = 0):
    """
    'type' is OFPAT_SET_TP_SRC/DST
    """
    self.type = type
    self.length = 8
    self.tp_port = tp_port

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHH", self.type, self.length, self.tp_port)
    packed += _PAD2
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.tp_port) = struct.unpack_from("!HHH", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.tp_port !=  other.tp_port: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'tp_port: ' + str(self.tp_port) + '\n'
    return outstr

class ofp_action_vendor_header (object):
  def __init__ (self, **kw):
    self.type = OFPAT_VENDOR
    self.length = 8
    self.vendor = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HHL", self.type, self.length, self.vendor)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.type, self.length, self.vendor) = struct.unpack_from("!HHL", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.type !=  other.type: return False
    if self.length !=  other.length: return False
    if self.vendor !=  other.vendor: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'len: ' + str(self.length) + '\n'
    outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
    return outstr

#3. Controller-to-Switch Messages

##3.1 Handshake
# was ofp_switch_features
class ofp_features_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.datapath_id = 0
    self.n_buffers = 0
    self.n_tables = 0
    self.capabilities = 0
    self.actions = 0
    self.ports = []
    
    initHelper(self, kw)
  
    self.header_type = OFPT_FEATURES_REPLY
    self.length = len(self)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!QLB", self.datapath_id, self.n_buffers, self.n_tables)
    packed += _PAD3
    packed += struct.pack("!LL", self.capabilities, self.actions)
    for i in self.ports:
      packed += i.pack(assertstruct)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 32):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.datapath_id, self.n_buffers, self.n_tables) = struct.unpack_from("!QLB", binaryString, 8)
    (self.capabilities, self.actions) = struct.unpack_from("!LL", binaryString, 24)
    portCount = (self.length - 32) / OFP_PHY_PORT_BYTES
    self.ports = []
    for i in xrange(0, portCount):
      p = ofp_phy_port()
      p.unpack(binaryString[32+i*OFP_PHY_PORT_BYTES:])
      self.ports.append(p)
    return binaryString[self.length:]

  def __len__ (self):
    l = 32
    for _ in self.ports:
      l += OFP_PHY_PORT_BYTES
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.datapath_id !=  other.datapath_id: return False
    if self.n_buffers !=  other.n_buffers: return False
    if self.n_tables !=  other.n_tables: return False
    if self.capabilities !=  other.capabilities: return False
    if self.actions !=  other.actions: return False
    if self.ports !=  other.ports: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'datapath_id: ' + str(self.datapath_id) + '\n'
    outstr += prefix + 'n_buffers: ' + str(self.n_buffers) + '\n'
    outstr += prefix + 'n_tables: ' + str(self.n_tables) + '\n'
    outstr += prefix + 'capabilities: ' + str(self.capabilities) + '\n'
    outstr += prefix + 'actions: ' + str(self.actions) + '\n'
    outstr += prefix + 'ports: \n'
    for obj in self.ports:
      outstr += obj.show(prefix + '  ')
    return outstr

ofp_switch_features = ofp_features_reply

ofp_capabilities_rev_map = {
  'OFPC_FLOW_STATS'   : 1,
  'OFPC_TABLE_STATS'  : 2,
  'OFPC_PORT_STATS'   : 4,
  'OFPC_STP'          : 8,
  'OFPC_RESERVED'     : 16,
  'OFPC_IP_REASM'     : 32,
  'OFPC_QUEUE_STATS'  : 64,
  'OFPC_ARP_MATCH_IP' : 128,
}

##3.2 Switch Configuration
class ofp_switch_config (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_SET_CONFIG
    self.length = 12
    self.flags = 0
    self.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.flags, self.miss_send_len)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.flags, self.miss_send_len) = struct.unpack_from("!HH", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    l = 12
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.flags !=  other.flags: return False
    if self.miss_send_len !=  other.miss_send_len: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'miss_send_len: ' + str(self.miss_send_len) + '\n'
    return outstr

ofp_config_flags_rev_map = {
  'OFPC_FRAG_NORMAL' : 0,
  'OFPC_FRAG_DROP'   : 1,
  'OFPC_FRAG_REASM'  : 2,
  'OFPC_FRAG_MASK'   : 3,
}

##3.3 Modify State Messages
class ofp_flow_mod (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_FLOW_MOD
    if 'match' in kw:
      self.match = None
    else:
      self.match = ofp_match()
    self.cookie = 0
    self.command = OFPFC_ADD
    self.idle_timeout = 0
    self.hard_timeout = 0
    self.priority = OFP_DEFAULT_PRIORITY
    self.buffer_id = -1
    self.out_port = OFPP_NONE
    self.flags = 0
    self.actions = []

    # ofp_flow_mod and ofp_packet_out do some special handling of 'actions'...

    # Allow "action" as a synonym for "actions"
    if 'action' in kw and 'actions' not in kw:
      kw['actions'] = kw['action']
      del kw['action']

    initHelper(self, kw)

    # Allow use of actions=<a single action> for kw args.
    if not hasattr(self.actions, '__getitem__'):
      self.actions = [self.actions]

  def _assert (self):
    if(not isinstance(self.match, ofp_match)):
      return (False, "match is not class ofp_match")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    self.length = len(self)
    packed += ofp_header.pack(self)
    packed += self.match.pack(flow_mod=True)
    packed += struct.pack("!QHHHHLHH", self.cookie, self.command, self.idle_timeout, self.hard_timeout, self.priority, self.buffer_id & 0xffffffff, self.out_port, self.flags)
    for i in self.actions:
      packed += i.pack(assertstruct)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 72):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    self.match.unpack(binaryString[8:], flow_mod=True)
    (self.cookie, self.command, self.idle_timeout, self.hard_timeout, self.priority, self.buffer_id, self.out_port, self.flags) = struct.unpack_from("!QHHHHLHH", binaryString, 8 + len(self.match))
    if self.buffer_id == 0xffffffff:
      self.buffer_id = -1
    self.actions, offset = _unpack_actions(binaryString, self.length-(32 + len(self.match)), 32 + len(self.match))
    assert offset == self.length
    return binaryString[offset:]

  def __len__ (self):
    l = 32 + len(self.match)
    for i in self.actions:
      l += len(i)#.length()
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.match !=  other.match: return False
    if self.cookie !=  other.cookie: return False
    if self.command !=  other.command: return False
    if self.idle_timeout !=  other.idle_timeout: return False
    if self.hard_timeout !=  other.hard_timeout: return False
    if self.priority !=  other.priority: return False
    if self.buffer_id !=  other.buffer_id: return False
    if self.out_port !=  other.out_port: return False
    if self.flags !=  other.flags: return False
    if self.actions !=  other.actions: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'match: \n'
    outstr += self.match.show(prefix + '  ')
    outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
    outstr += prefix + 'command: ' + str(self.command) + '\n'
    outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
    outstr += prefix + 'hard_timeout: ' + str(self.hard_timeout) + '\n'
    outstr += prefix + 'priority: ' + str(self.priority) + '\n'
    outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
    outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'actions: \n'
    for obj in self.actions:
      outstr += obj.show(prefix + '  ')
    return outstr

ofp_flow_mod_command_rev_map = {
  'OFPFC_ADD'           : 0,
  'OFPFC_MODIFY'        : 1,
  'OFPFC_MODIFY_STRICT' : 2,
  'OFPFC_DELETE'        : 3,
  'OFPFC_DELETE_STRICT' : 4,
}

ofp_flow_mod_flags_rev_map = {
  'OFPFF_SEND_FLOW_REM' : 1,
  'OFPFF_CHECK_OVERLAP' : 2,
  'OFPFF_EMERG'         : 4,
}

class ofp_port_mod (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_PORT_MOD
    self.port_no = 0
    self.hw_addr = EMPTY_ETH
    self.config = 0
    self.mask = 0
    self.advertise = 0
    self.length = 32

    initHelper(self, kw)

  def _assert (self):
    if not isinstance(self.hw_addr, bytes) and not isinstance(self.hw_addr, EthAddr):
      return (False, "hw_addr is not bytes or EthAddr")
    if len(self.hw_addr) != 6:
      return (False, "hw_addr is not of size 6")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!H", self.port_no)
    if isinstance(self.hw_addr, bytes):
      packed += self.hw_addr
    else:
      packed += self.hw_addr.toRaw()
    packed += struct.pack("!LLL", self.config, self.mask, self.advertise)
    packed += _PAD4
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 32):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.port_no,) = struct.unpack_from("!H", binaryString, 8)
    self.hw_addr = EthAddr(binaryString[10:16])
    (self.config, self.mask, self.advertise) = struct.unpack_from("!LLL", binaryString, 16)
    return binaryString[32:]

  def __len__ (self):
    return 32

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.port_no !=  other.port_no: return False
    if self.hw_addr !=  other.hw_addr: return False
    if self.config !=  other.config: return False
    if self.mask !=  other.mask: return False
    if self.advertise !=  other.advertise: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    outstr += prefix + 'hw_addr: ' + str(EthAddr(self.hw_addr)) + '\n'
    outstr += prefix + 'config: ' + str(self.config) + '\n'
    outstr += prefix + 'mask: ' + str(self.mask) + '\n'
    outstr += prefix + 'advertise: ' + str(self.advertise) + '\n'
    return outstr

##3.4 Queue Configuration Messages
class ofp_queue_get_config_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_QUEUE_GET_CONFIG_REQUEST
    self.port = 0
    self.length = 12
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!H", self.port)
    packed += _PAD2
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.port,) = struct.unpack_from("!H", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    return 12

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.port !=  other.port: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'port: ' + str(self.port) + '\n'
    return outstr

class ofp_queue_get_config_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_QUEUE_GET_CONFIG_REPLY
    self.length = 16
    self.port = 0
    self.queues = []

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!H", self.port)
    packed += _PAD6
    for i in self.queues:
      packed += i.pack(assertstruct)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 16):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.port,) = struct.unpack_from("!H", binaryString, 8)
    return binaryString[16:]

  def __len__ (self):
    l = 16
    for i in self.queues:
      l += len(i)
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.port !=  other.port: return False
    if self.queues !=  other.queues: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'port: ' + str(self.port) + '\n'
    outstr += prefix + 'queues: \n'
    for obj in self.queues:
      outstr += obj.show(prefix + '  ')
    return outstr

##3.5 Read State Messages
class ofp_stats_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_STATS_REQUEST
    self.type = None # Try to guess
    self.flags = 0
    self.body = b''
    self._body_data = (None, None)

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    self.length = len(self)
    if self.type is None:
      if isinstance(self.body, ofp_flow_stats_request):
        self.type = OFPST_FLOW
      elif isinstance(self.body, ofp_aggregate_stats_request):
        self.type = OFPST_AGGREGATE
      elif self.body_data == b'':
        self.type = OFPST_DESC # Maybe shouldn't assume this?
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.type, self.flags)
    packed += self.body_data
    return packed

  @property
  def body_data (self):
    if self._body_data[0] is not self.body:
      if hasattr(self.body, 'pack'):
        self._body_data = (self.body, self.body.pack())
      else:
        self._body_data = (self.body, self.body)
    return self._body_data[1]

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.type, self.flags) = struct.unpack_from("!HH", binaryString, 8)
    self.body = binaryString[12:self.length]
    assert self.length == len(self)
    return binaryString[self.length:]

  def __len__ (self):
    return 12 + len(self.body_data)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.type != other.type: return False
    if self.flags != other.flags: return False
    if self.body_data != other.body_data: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'body:\n' + _format_body(self.body, prefix + '  ') + '\n'
    return outstr

class ofp_stats_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_STATS_REPLY
    self.type = 0
    self.flags = 0
    self.body = b''
    self._body_data = (None, None)

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  @property
  def body_data (self):
    if self._body_data[0] is not self.body:
      def _pack(b):
        return b.pack() if hasattr(b, 'pack') else b

      data = b''
      if isinstance(self.body, collections.Iterable):
        for b in self.body:
          data += _pack(b)
      else:
        data = _pack(self.body)
      self._body_data = (self.body, data)
    return self._body_data[1]

  def pack (self, assertstruct=True):
    if type == None or type == 0 and type(self.body) in ofp_stats_reply_class_to_type_map:
      self.type = ofp_stats_reply_class_to_type_map[type(self.body)]

    self.length = len(self)
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.type, self.flags)
    packed += self.body_data
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.type, self.flags) = struct.unpack_from("!HH", binaryString, 8)
    self.body = binaryString[12:self.length]
    return binaryString[self.length:]

  def __len__ (self):
    l = 12
    l += len(self.body)
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.type !=  other.type: return False
    if self.flags !=  other.flags: return False
    if self.body !=  other.body: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'type: ' + str(self.type) + '\n'
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'body:\n' + _format_body(self.body, prefix + '  ') + '\n'
    return outstr

ofp_stats_types_rev_map = {
  'OFPST_DESC'      : 0,
  'OFPST_FLOW'      : 1,
  'OFPST_AGGREGATE' : 2,
  'OFPST_TABLE'     : 3,
  'OFPST_PORT'      : 4,
  'OFPST_QUEUE'     : 5,
  'OFPST_VENDOR'    : 65535,
}

ofp_stats_reply_flags_rev_map = {
  'OFPSF_REPLY_MORE' : 1,
}

class ofp_desc_stats (object):
  def __init__ (self, **kw):
    self.mfr_desc= ""
    self.hw_desc= ""
    self.sw_desc= ""
    self.serial_num= ""
    self.dp_desc= ""

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.mfr_desc, str)):
      return (False, "mfr_desc is not string")
    if(len(self.mfr_desc) > 256):
      return (False, "mfr_desc is not of size 256")
    if(not isinstance(self.hw_desc, str)):
      return (False, "hw_desc is not string")
    if(len(self.hw_desc) > 256):
      return (False, "hw_desc is not of size 256")
    if(not isinstance(self.sw_desc, str)):
      return (False, "sw_desc is not string")
    if(len(self.sw_desc) > 256):
      return (False, "sw_desc is not of size 256")
    if(not isinstance(self.serial_num, str)):
      return (False, "serial_num is not string")
    if(len(self.serial_num) > 32):
      return (False, "serial_num is not of size 32")
    if(not isinstance(self.dp_desc, str)):
      return (False, "dp_desc is not string")
    if(len(self.dp_desc) > 256):
      return (False, "dp_desc is not of size 256")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += self.mfr_desc.ljust(256,'\0')
    packed += self.hw_desc.ljust(256,'\0')
    packed += self.sw_desc.ljust(256,'\0')
    packed += self.serial_num.ljust(32,'\0')
    packed += self.dp_desc.ljust(256,'\0')
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 1056):
      return binaryString
    self.mfr_desc = binaryString[0:256].replace("\0","")
    self.hw_desc = binaryString[256:512].replace("\0","")
    self.sw_desc = binaryString[512:768].replace("\0","")
    self.serial_num = binaryString[768:800].replace("\0","")
    self.dp_desc = binaryString[800:1056].replace("\0","")
    return binaryString[1056:]

  def __len__ (self):
    return 1056

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.mfr_desc !=  other.mfr_desc: return False
    if self.hw_desc !=  other.hw_desc: return False
    if self.sw_desc !=  other.sw_desc: return False
    if self.serial_num !=  other.serial_num: return False
    if self.dp_desc !=  other.dp_desc: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'mfr_desc: ' + str(self.mfr_desc) + '\n'
    outstr += prefix + 'hw_desc: ' + str(self.hw_desc) + '\n'
    outstr += prefix + 'sw_desc: ' + str(self.sw_desc) + '\n'
    outstr += prefix + 'serial_num: ' + str(self.serial_num) + '\n'
    outstr += prefix + 'dp_desc: ' + str(self.dp_desc) + '\n'
    return outstr

class ofp_flow_stats_request (object):
  def __init__ (self, **kw):
    self.match = ofp_match()
    self.table_id = TABLE_ALL
    self.out_port = OFPP_NONE
    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.match, ofp_match)):
      return (False, "match is not class ofp_match")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += self.match.pack()
    packed += struct.pack("!BBH", self.table_id, 0, self.out_port)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 44):
      return binaryString
    self.match.unpack(binaryString[0:])
    (self.table_id, pad, self.out_port) = struct.unpack_from("!BBH", binaryString, len(self.match))
    return binaryString[len(self)]

  def __len__ (self):
    return 4 + len(self.match)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.match !=  other.match: return False
    if self.table_id !=  other.table_id: return False
    if self.out_port !=  other.out_port: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'match: \n'
    self.match.show(prefix + '  ')
    outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
    outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
    return outstr

class ofp_flow_stats (object):
  def __init__ (self, **kw):
    self.length = 0
    self.table_id = 0
    self.match = ofp_match()
    self.duration_sec = 0
    self.duration_nsec = 0
    self.priority = OFP_DEFAULT_PRIORITY
    self.idle_timeout = 0
    self.hard_timeout = 0
    self.cookie = 0
    self.packet_count = 0
    self.byte_count = 0
    self.actions = []

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.match, ofp_match)):
      return (False, "match is not class ofp_match")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!HBB", self.length, self.table_id, 0)
    packed += self.match.pack()
    packed += struct.pack("!LLHHH", self.duration_sec, self.duration_nsec, self.priority, self.idle_timeout, self.hard_timeout)
    packed += _PAD6 # Pad
    packed += struct.pack("!QQQ", self.cookie, self.packet_count, self.byte_count)
    for i in self.actions:
      packed += i.pack(assertstruct)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 48 + len(self.match)):
      return binaryString
    (self.length, self.table_id, pad) = struct.unpack_from("!HBB", binaryString, 0)
    self.match.unpack(binaryString[4:])
    (self.duration_sec, self.duration_nsec, self.priority, self.idle_timeout, self.hard_timeout) = struct.unpack_from("!LLHHH", binaryString, 4 + len(self.match))
    (self.cookie, self.packet_count, self.byte_count) = struct.unpack_from("!QQQ", binaryString, 24 + len(self.match))
    self.actions,offset = _unpack_actions(binaryString, self.length - (48 + len(self.match)), 48 + len(self.match))
    assert offset == self.length
    assert self.length == len(self)
    return binaryString[offset:]

  def __len__ (self):
    l = 48 + len(self.match)
    for i in self.actions:
      l += len(i)
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.length !=  other.length: return False
    if self.table_id !=  other.table_id: return False
    if self.match !=  other.match: return False
    if self.duration_sec !=  other.duration_sec: return False
    if self.duration_nsec !=  other.duration_nsec: return False
    if self.priority !=  other.priority: return False
    if self.idle_timeout !=  other.idle_timeout: return False
    if self.hard_timeout !=  other.hard_timeout: return False
    if self.cookie !=  other.cookie: return False
    if self.packet_count !=  other.packet_count: return False
    if self.byte_count !=  other.byte_count: return False
    if self.actions !=  other.actions: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'length: ' + str(self.length) + '\n'
    outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
    outstr += prefix + 'match: \n'
    self.match.show(prefix + '  ')
    outstr += prefix + 'duration_sec: ' + str(self.duration_sec) + '\n'
    outstr += prefix + 'duration_nsec: ' + str(self.duration_nsec) + '\n'
    outstr += prefix + 'priority: ' + str(self.priority) + '\n'
    outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
    outstr += prefix + 'hard_timeout: ' + str(self.hard_timeout) + '\n'
    outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
    outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
    outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
    outstr += prefix + 'actions: \n'
    for obj in self.actions:
      outstr += obj.show(prefix + '  ')
    return outstr

class ofp_aggregate_stats_request (object):
  def __init__ (self, **kw):
    self.match = ofp_match()
    self.table_id = TABLE_ALL
    self.out_port = OFPP_NONE

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.match, ofp_match)):
      return (False, "match is not class ofp_match")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += self.match.pack()
    packed += struct.pack("!BBH", self.table_id, 0, self.out_port)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 4 + len(self.match)):
      return binaryString
    self.match.unpack(binaryString[0:])
    (self.table_id, pad, self.out_port) = struct.unpack_from("!BBH", binaryString, len(self.match))
    return binaryString[44:]

  def __len__ (self):
    return 44

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.match !=  other.match: return False
    if self.table_id !=  other.table_id: return False
    if self.out_port !=  other.out_port: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'match: \n'
    self.match.show(prefix + '  ')
    outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
    outstr += prefix + 'out_port: ' + str(self.out_port) + '\n'
    return outstr

class ofp_aggregate_stats (object):
  def __init__ (self, **kw):
    self.packet_count = 0
    self.byte_count = 0
    self.flow_count = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!QQL", self.packet_count, self.byte_count, self.flow_count)
    packed += _PAD4 # Pad
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 24):
      return binaryString
    (self.packet_count, self.byte_count, self.flow_count) = struct.unpack_from("!QQL", binaryString, 0)
    return binaryString[24:]

  def __len__ (self):
    return 24

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.packet_count !=  other.packet_count: return False
    if self.byte_count !=  other.byte_count: return False
    if self.flow_count !=  other.flow_count: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
    outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
    outstr += prefix + 'flow_count: ' + str(self.flow_count) + '\n'
    return outstr
ofp_aggregate_stats_reply = ofp_aggregate_stats

class ofp_table_stats (object):
  def __init__ (self, **kw):
    self.table_id = 0
    self.name= ""
    self.wildcards = 0
    self.max_entries = 0
    self.active_count = 0
    self.lookup_count = 0
    self.matched_count = 0

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.name, str)):
      return (False, "name is not string")
    if(len(self.name) > 32):
      return (False, "name is not of size 32")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!B", self.table_id)
    packed += _PAD3
    packed += self.name.ljust(32,'\0')
    packed += struct.pack("!LLLQQ", self.wildcards, self.max_entries, self.active_count, self.lookup_count, self.matched_count)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 64):
      return binaryString
    (self.table_id,) = struct.unpack_from("!B", binaryString, 0)
    self.name = binaryString[4:36].replace("\0","")
    (self.wildcards, self.max_entries, self.active_count, self.lookup_count, self.matched_count) = struct.unpack_from("!LLLQQ", binaryString, 36)
    return binaryString[64:]

  def __len__ (self):
    return 64

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.table_id !=  other.table_id: return False
    if self.name !=  other.name: return False
    if self.wildcards !=  other.wildcards: return False
    if self.max_entries !=  other.max_entries: return False
    if self.active_count !=  other.active_count: return False
    if self.lookup_count !=  other.lookup_count: return False
    if self.matched_count !=  other.matched_count: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'table_id: ' + str(self.table_id) + '\n'
    outstr += prefix + 'name: ' + str(self.name) + '\n'
    outstr += prefix + 'wildcards: ' + str(self.wildcards) + '\n'
    outstr += prefix + 'max_entries: ' + str(self.max_entries) + '\n'
    outstr += prefix + 'active_count: ' + str(self.active_count) + '\n'
    outstr += prefix + 'lookup_count: ' + str(self.lookup_count) + '\n'
    outstr += prefix + 'matched_count: ' + str(self.matched_count) + '\n'
    return outstr

class ofp_port_stats_request (object):
  def __init__ (self, **kw):
    self.port_no = 0
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!H", self.port_no)
    packed += _PAD6
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.port_no,) = struct.unpack_from("!H", binaryString, 0)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.port_no !=  other.port_no: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    return outstr

class ofp_port_stats (object):
  def __init__ (self, **kw):
    self.port_no = 0
    self.rx_packets = 0
    self.tx_packets = 0
    self.rx_bytes = 0
    self.tx_bytes = 0
    self.rx_dropped = 0
    self.tx_dropped = 0
    self.rx_errors = 0
    self.tx_errors = 0
    self.rx_frame_err = 0
    self.rx_over_err = 0
    self.rx_crc_err = 0
    self.collisions = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!H", self.port_no)
    packed += _PAD6
    packed += struct.pack("!QQQQQQQQQQQQ", self.rx_packets, self.tx_packets, self.rx_bytes, self.tx_bytes, self.rx_dropped, self.tx_dropped, self.rx_errors, self.tx_errors, self.rx_frame_err, self.rx_over_err, self.rx_crc_err, self.collisions)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 104):
      return binaryString
    (self.port_no,) = struct.unpack_from("!H", binaryString, 0)
    (self.rx_packets, self.tx_packets, self.rx_bytes, self.tx_bytes, self.rx_dropped, self.tx_dropped, self.rx_errors, self.tx_errors, self.rx_frame_err, self.rx_over_err, self.rx_crc_err, self.collisions) = struct.unpack_from("!QQQQQQQQQQQQ", binaryString, 8)
    return binaryString[104:]

  def __len__ (self):
    return 104

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.port_no !=  other.port_no: return False
    if self.rx_packets !=  other.rx_packets: return False
    if self.tx_packets !=  other.tx_packets: return False
    if self.rx_bytes !=  other.rx_bytes: return False
    if self.tx_bytes !=  other.tx_bytes: return False
    if self.rx_dropped !=  other.rx_dropped: return False
    if self.tx_dropped !=  other.tx_dropped: return False
    if self.rx_errors !=  other.rx_errors: return False
    if self.tx_errors !=  other.tx_errors: return False
    if self.rx_frame_err !=  other.rx_frame_err: return False
    if self.rx_over_err !=  other.rx_over_err: return False
    if self.rx_crc_err !=  other.rx_crc_err: return False
    if self.collisions !=  other.collisions: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def __add__(self, other):
    if type(self) != type(other): raise NotImplemented()
    return ofp_port_stats(
        port_no=OFPP_NONE,
        rx_packets = self.rx_packets + other.rx_packets,
        tx_packets = self.tx_packets + other.tx_packets,
        rx_bytes = self.rx_bytes + other.rx_bytes,
        tx_bytes = self.tx_bytes + other.tx_bytes,
        rx_dropped = self.rx_dropped + other.rx_dropped,
        tx_dropped = self.tx_dropped + other.tx_dropped,
        rx_errors = self.rx_errors + other.rx_errors,
        tx_errors = self.tx_errors + other.tx_errors,
        rx_frame_err = self.rx_frame_err + other.rx_frame_err,
        rx_over_err = self.rx_over_err + other.rx_over_err,
        rx_crc_err = self.rx_crc_err + other.rx_crc_err,
        collisions = self.collisions + other.collisions)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    outstr += prefix + 'rx_packets: ' + str(self.rx_packets) + '\n'
    outstr += prefix + 'tx_packets: ' + str(self.tx_packets) + '\n'
    outstr += prefix + 'rx_bytes: ' + str(self.rx_bytes) + '\n'
    outstr += prefix + 'tx_bytes: ' + str(self.tx_bytes) + '\n'
    outstr += prefix + 'rx_dropped: ' + str(self.rx_dropped) + '\n'
    outstr += prefix + 'tx_dropped: ' + str(self.tx_dropped) + '\n'
    outstr += prefix + 'rx_errors: ' + str(self.rx_errors) + '\n'
    outstr += prefix + 'tx_errors: ' + str(self.tx_errors) + '\n'
    outstr += prefix + 'rx_frame_err: ' + str(self.rx_frame_err) + '\n'
    outstr += prefix + 'rx_over_err: ' + str(self.rx_over_err) + '\n'
    outstr += prefix + 'rx_crc_err: ' + str(self.rx_crc_err) + '\n'
    outstr += prefix + 'collisions: ' + str(self.collisions) + '\n'
    return outstr

class ofp_queue_stats_request (object):
  def __init__ (self, **kw):
    self.port_no = 0
    self.queue_id = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!H", self.port_no)
    packed += _PAD2
    packed += struct.pack("!L", self.queue_id)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    (self.port_no,) = struct.unpack_from("!H", binaryString, 0)
    (self.queue_id,) = struct.unpack_from("!L", binaryString, 4)
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.port_no !=  other.port_no: return False
    if self.queue_id !=  other.queue_id: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
    return outstr

class ofp_queue_stats (object):
  def __init__ (self, **kw):
    self.port_no = 0
    self.queue_id = 0
    self.tx_bytes = 0
    self.tx_packets = 0
    self.tx_errors = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += struct.pack("!H", self.port_no)
    packed += _PAD2
    packed += struct.pack("!LQQQ", self.queue_id, self.tx_bytes, self.tx_packets, self.tx_errors)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 32):
      return binaryString
    (self.port_no,) = struct.unpack_from("!H", binaryString, 0)
    (self.queue_id, self.tx_bytes, self.tx_packets, self.tx_errors) = struct.unpack_from("!LQQQ", binaryString, 4)
    return binaryString[32:]

  def __len__ (self):
    return 32

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if self.port_no !=  other.port_no: return False
    if self.queue_id !=  other.queue_id: return False
    if self.tx_bytes !=  other.tx_bytes: return False
    if self.tx_packets !=  other.tx_packets: return False
    if self.tx_errors !=  other.tx_errors: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'port_no: ' + str(self.port_no) + '\n'
    outstr += prefix + 'queue_id: ' + str(self.queue_id) + '\n'
    outstr += prefix + 'tx_bytes: ' + str(self.tx_bytes) + '\n'
    outstr += prefix + 'tx_packets: ' + str(self.tx_packets) + '\n'
    outstr += prefix + 'tx_errors: ' + str(self.tx_errors) + '\n'
    return outstr

ofp_stats_reply_class_to_type_map = {
    ofp_desc_stats : ofp_stats_types_rev_map['OFPST_DESC'],
    ofp_flow_stats : ofp_stats_types_rev_map['OFPST_FLOW'],
    ofp_aggregate_stats : ofp_stats_types_rev_map['OFPST_AGGREGATE'],
    ofp_table_stats : ofp_stats_types_rev_map['OFPST_TABLE'],
    ofp_port_stats : ofp_stats_types_rev_map['OFPST_PORT'],
    ofp_queue_stats : ofp_stats_types_rev_map['OFPST_QUEUE']
}


##3.6 Send Packet Message
class ofp_packet_out (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_PACKET_OUT
    self.buffer_id = -1
    self.in_port = OFPP_NONE
    self.actions = []
    self._data = ''

    # ofp_flow_mod and ofp_packet_out do some special handling of 'actions'...

    # Allow "action" as a synonym for "actions"
    if 'action' in kw and 'actions' not in kw:
      kw['actions'] = kw['action']
      del kw['action']
    initHelper(self, kw)

    # Allow use of actions=<a single action> for kw args.
    if not hasattr(self.actions, '__getitem__'):
      self.actions = [self.actions]

  def _set_data(self, data):
    assert_type("data", data, (packet_base, str))
    if data is None:
      self._data = ''
    elif isinstance(data, packet_base):
      self._data = data.pack()
    else:
      self._data = data
  def _get_data(self):
    return self._data
  data = property(_get_data, _set_data)

  def _assert (self):
    if self.buffer_id != -1 and self.data != '':
      return "can not have both buffer_id and data set"
    return True

  def pack (self, assertstruct=True):
    if(assertstruct):
      if self._assert() is not True:
        raise RuntimeError(self._assert())

    actions = b''.join((i.pack(assertstruct) for i in self.actions))
    actions_len = len(actions)

    self.length = 16 + actions_len
    if self.data is not None:
      self.length += len(self.data)

    if self.data is not None:
      return b''.join((ofp_header.pack(self),
      struct.pack("!LHH", self.buffer_id & 0xffFFffFF, self.in_port, actions_len),
      actions,
      self.data))
    else:
      return b''.join((ofp_header.pack(self),
      struct.pack("!LHH", self.buffer_id & 0xffFFffFF, self.in_port, actions_len),
      actions))

  def unpack (self, binaryString):
    if (len(binaryString) < 16):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.buffer_id, self.in_port, actions_len) = struct.unpack_from("!LHH", binaryString, 8)
    if self.buffer_id == 0xffFFffFF:
      self.buffer_id = -1
    self.actions,offset = _unpack_actions(binaryString, actions_len, 16)

    self.data = binaryString[offset:self.length] if offset < self.length else None
    return binaryString[self.length:]

  def __len__ (self):
    return 16 + reduce(operator.add, (a.length for a in self.actions), 0) + (len(self.data) if self.data else 0)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.buffer_id !=  other.buffer_id: return False
    if self.in_port !=  other.in_port: return False
    if self.actions !=  other.actions: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
    outstr += prefix + 'in_port: ' + str(self.in_port) + '\n'
    outstr += prefix + 'actions_len: ' + str(len(self.actions)) + '\n'
    outstr += prefix + 'actions: \n'
    for obj in self.actions:
      if obj is None:
        raise RuntimeError("An element of self.actions was None! Bad formatting...")
      outstr += obj.show(prefix + '  ')
    return outstr

##3.7 Barrier Message
class ofp_barrier_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_BARRIER_REPLY
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        raise RuntimeError("assertstruct failed")
    packed = ""
    packed += ofp_header.pack(self)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    return outstr

class ofp_barrier_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_BARRIER_REQUEST

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    return outstr

#4 Asynchronous Messages
class ofp_packet_in (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)

    self.in_port = OFPP_NONE
    self.buffer_id = -1
    self.reason = 0
    self.data = None

    initHelper(self, kw)

    self.header_type = OFPT_PACKET_IN
    self._total_len = 0

  def _set_data(self, data):
    assert_type("data", data, (packet_base, str))
    if data is None:
      self._data = ''
    elif isinstance(data, packet_base):
      self._data = data.pack()
    else:
      self._data = data
  def _get_data(self):
    return self._data
  data = property(_get_data, _set_data)

  def _assert (self):
    if not isinstance(self.data, str):
      return (False,
          "ofp_packet_in: data should be raw byte string, not %s" % str(type(self.data)))
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        raise AssertionError(self._assert()[1])
    packed = ""
    # need to update the self.length field for ofp_header.pack to put the correct value in the packed
    # array. this sucks.
    self.length = len(self)
    self._total_len = len(self) # TODO: Is this correct?
    packed += ofp_header.pack(self)
    packed += struct.pack("!LHHBB", self.buffer_id & 0xffFFffFF, self._total_len, self.in_port, self.reason, 0)
    packed += self.data
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 18):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.buffer_id, self._total_len, self.in_port, self.reason, pad) = struct.unpack_from("!LHHBB", binaryString, 8)
    if self.buffer_id == 0xFFffFFff:
      self.buffer_id = -1
    if (len(binaryString) < self.length):
      return binaryString
    self.data = binaryString[18:self.length]
    return binaryString[self.length:]

  def __len__ (self):
    l = 18
    l += len(self.data)*1
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.buffer_id !=  other.buffer_id: return False
    if self._total_len !=  other._total_len: return False
    if self.in_port !=  other.in_port: return False
    if self.reason !=  other.reason: return False
    if self.data !=  other.data: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'buffer_id: ' + str(self.buffer_id) + '\n'
    outstr += prefix + '_total_len: ' + str(self._total_len) + '\n'
    outstr += prefix + 'in_port: ' + str(self.in_port) + '\n'
    outstr += prefix + 'reason: ' + str(self.reason) + '\n'
    outstr += prefix + 'data: ' + str(self.data) + '\n'
    return outstr

ofp_packet_in_reason_rev_map = {
  'OFPR_NO_MATCH' : 0,
  'OFPR_ACTION'   : 1,
}

class ofp_flow_removed (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_FLOW_REMOVED
    self.match = ofp_match()
    self.cookie = 0
    self.priority = 0
    self.reason = 0
    self.duration_sec = 0
    self.duration_nsec = 0
    self.idle_timeout = 0
    self.packet_count = 0
    self.byte_count = 0
    self.length = len(self)
    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.match, ofp_match)):
      return (False, "match is not class ofp_match")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += self.match.pack()
    packed += struct.pack("!QHB", self.cookie, self.priority, self.reason)
    packed += _PAD
    packed += struct.pack("!LLH", self.duration_sec, self.duration_nsec, self.idle_timeout)
    packed += _PAD2
    packed += struct.pack("!QQ", self.packet_count, self.byte_count)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < len(self)):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    self.match.unpack(binaryString[8:])
    (self.cookie, self.priority, self.reason) = struct.unpack_from("!QHB", binaryString, 8 + len(self.match))
    (self.duration_sec, self.duration_nsec, self.idle_timeout) = struct.unpack_from("!LLH", binaryString, 20 + len(self.match))
    (self.packet_count, self.byte_count) = struct.unpack_from("!QQ", binaryString, 32 + len(self.match))
    return binaryString[len(self):]

  def __len__ (self):
    return 48 + len(self.match)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.match !=  other.match: return False
    if self.cookie !=  other.cookie: return False
    if self.priority !=  other.priority: return False
    if self.reason !=  other.reason: return False
    if self.duration_sec !=  other.duration_sec: return False
    if self.duration_nsec !=  other.duration_nsec: return False
    if self.idle_timeout !=  other.idle_timeout: return False
    if self.packet_count !=  other.packet_count: return False
    if self.byte_count !=  other.byte_count: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'match: \n'
    self.match.show(prefix + '  ')
    outstr += prefix + 'cookie: ' + str(self.cookie) + '\n'
    outstr += prefix + 'priority: ' + str(self.priority) + '\n'
    outstr += prefix + 'reason: ' + str(self.reason) + '\n'
    outstr += prefix + 'duration_sec: ' + str(self.duration_sec) + '\n'
    outstr += prefix + 'duration_nsec: ' + str(self.duration_nsec) + '\n'
    outstr += prefix + 'idle_timeout: ' + str(self.idle_timeout) + '\n'
    outstr += prefix + 'packet_count: ' + str(self.packet_count) + '\n'
    outstr += prefix + 'byte_count: ' + str(self.byte_count) + '\n'
    return outstr

ofp_flow_removed_reason_rev_map = {
  'OFPRR_IDLE_TIMEOUT' : 0,
  'OFPRR_HARD_TIMEOUT' : 1,
  'OFPRR_DELETE'       : 2,
}

class ofp_port_status (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_PORT_STATUS
    self.reason = 0
    self.desc = ofp_phy_port()
    self.length = 64

    initHelper(self, kw)

  def _assert (self):
    if(not isinstance(self.desc, ofp_phy_port)):
      return (False, "desc is not class ofp_phy_port")
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!B", self.reason)
    packed += _PAD * 7 # Pad
    packed += self.desc.pack()
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 64):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.reason,) = struct.unpack_from("!B", binaryString, 8)
    self.desc.unpack(binaryString[16:])
    return binaryString[64:]

  def __len__ (self):
    return 64

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.reason !=  other.reason: return False
    if self.desc !=  other.desc: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'reason: ' + str(self.reason) + '\n'
    outstr += prefix + 'desc: \n'
    self.desc.show(prefix + '  ')
    return outstr

ofp_port_reason_rev_map = {
  'OFPPR_ADD'    : 0,
  'OFPPR_DELETE' : 1,
  'OFPPR_MODIFY' : 2,
}

class ofp_error (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_ERROR
    self.type = 0
    self.code = 0
    self.data = []

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    self.length = len(self)
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.type, self.code)
    for i in self.data:
      packed += struct.pack("!B",i)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.type, self.code) = struct.unpack_from("!HH", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    l = 12
    l += len(self.data)*1
    return l

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.type !=  other.type: return False
    if self.code !=  other.code: return False
    if self.data !=  other.data: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    t = self.type
    c = self.code
    if t < len(ofp_error_type):
      n = ofp_error_type_map[t]
      t = "%s (%i)" % (n, t)
      n = 'ofp' + n.lower()[5:] + '_code_map'
      if n in sys.modules[__name__].__dict__:
        if c in sys.modules[__name__].__dict__[n]:
          c = "%s (%i)" % (sys.modules[__name__].__dict__[n][c], c)
    outstr += prefix + 'type: ' + str(t) + '\n'
    outstr += prefix + 'code: ' + str(c) + '\n'
    if len(self.data):
      outstr += prefix + 'data: ' + str(self.data) + '\n'
    return outstr.strip()

ofp_error_type_rev_map = {
  'OFPET_HELLO_FAILED'    : 0,
  'OFPET_BAD_REQUEST'     : 1,
  'OFPET_BAD_ACTION'      : 2,
  'OFPET_FLOW_MOD_FAILED' : 3,
  'OFPET_PORT_MOD_FAILED' : 4,
  'OFPET_QUEUE_OP_FAILED' : 5,
}

ofp_hello_failed_code_rev_map = {
  'OFPHFC_INCOMPATIBLE' : 0,
  'OFPHFC_EPERM'        : 1,
}

ofp_bad_request_code_rev_map = {
  'OFPBRC_BAD_VERSION'    : 0,
  'OFPBRC_BAD_TYPE'       : 1,
  'OFPBRC_BAD_STAT'       : 2,
  'OFPBRC_BAD_VENDOR'     : 3,
  'OFPBRC_BAD_SUBTYPE'    : 4,
  'OFPBRC_EPERM'          : 5,
  'OFPBRC_BAD_LEN'        : 6,
  'OFPBRC_BUFFER_EMPTY'   : 7,
  'OFPBRC_BUFFER_UNKNOWN' : 8,
}

ofp_bad_action_code_rev_map = {
  'OFPBAC_BAD_TYPE'        : 0,
  'OFPBAC_BAD_LEN'         : 1,
  'OFPBAC_BAD_VENDOR'      : 2,
  'OFPBAC_BAD_VENDOR_TYPE' : 3,
  'OFPBAC_BAD_OUT_PORT'    : 4,
  'OFPBAC_BAD_ARGUMENT'    : 5,
  'OFPBAC_EPERM'           : 6,
  'OFPBAC_TOO_MANY'        : 7,
  'OFPBAC_BAD_QUEUE'       : 8,
}

ofp_flow_mod_failed_code_rev_map = {
  'OFPFMFC_ALL_TABLES_FULL'   : 0,
  'OFPFMFC_OVERLAP'           : 1,
  'OFPFMFC_EPERM'             : 2,
  'OFPFMFC_BAD_EMERG_TIMEOUT' : 3,
  'OFPFMFC_BAD_COMMAND'       : 4,
  'OFPFMFC_UNSUPPORTED'       : 5,
}

ofp_port_mod_failed_code_rev_map = {
  'OFPPMFC_BAD_PORT'    : 0,
  'OFPPMFC_BAD_HW_ADDR' : 1,
}

ofp_queue_op_failed_code_rev_map = {
  'OFPQOFC_BAD_PORT'  : 0,
  'OFPQOFC_BAD_QUEUE' : 1,
  'OFPQOFC_EPERM'     : 2,
}

#5. Symmetric Messages
class ofp_hello (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_HELLO
    self.length = len(self)
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    return outstr

class ofp_echo_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_ECHO_REQUEST
    self.body = b''
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = b""
    packed += ofp_header.pack(self)
    packed += self.body
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    # Note that we trust the header to be correct here
    if len(binaryString) < self.length:
      return binaryString
    l = self.length - 8
    self.body = binaryString[8:8+l]
    return binaryString[8 + l:]

  def __len__ (self):
    return 8 + len(self.body)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.body !=  other.body: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'body:\n' + _format_body(self.body, prefix + '  ') + '\n'
    return outstr

class ofp_echo_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_ECHO_REPLY
    self.body = b''
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = b""
    packed += ofp_header.pack(self)
    packed += self.body
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    # Note that we trust the header to be correct here
    if len(binaryString) < self.length:
      return binaryString
    l = self.length - 8
    self.body = binaryString[8:8+l]
    return binaryString[8 + l:]

  def __len__ (self):
    return 8 + len(self.body)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.body !=  other.body: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'body:\n' + _format_body(self.body, prefix + '  ') + '\n'
    return outstr

class ofp_vendor_header (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_VENDOR
    self.vendor = 0

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!L", self.vendor)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.vendor,) = struct.unpack_from("!L", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    return 12

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.vendor !=  other.vendor: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
    return outstr

class ofp_vendor (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_VENDOR
    self.vendor = 0
    self.data = b''
    self.length = 12
    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    self.length = 12 + len(self.data)
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!L", self.vendor)
    if hasattr(self.data, "pack"):
      packed += self.data.pack()
    else:
      packed += self.data
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.vendor,) = struct.unpack_from("!L", binaryString, 8)
    if len(binaryString) < self.length:
      return binaryString
    self.data = binaryString[12:self.length]
    return binaryString[self.length:]

  def __len__ (self):
    return 12 + len(self.data)

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.vendor !=  other.vendor: return False
    if self.data != other.data: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'vendor: ' + str(self.vendor) + '\n'
    outstr += prefix + 'data: ' + self.data + '\n'
    return outstr

class ofp_features_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_FEATURES_REQUEST
    self.length = 8

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    return outstr

class ofp_get_config_request (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_GET_CONFIG_REQUEST

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 8):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    return binaryString[8:]

  def __len__ (self):
    return 8

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    return outstr

class ofp_get_config_reply (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_GET_CONFIG_REPLY
    self.flags = 0
    self.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN
    self.length = 12

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.flags, self.miss_send_len)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.flags, self.miss_send_len) = struct.unpack_from("!HH", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    return 12

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.flags !=  other.flags: return False
    if self.miss_send_len !=  other.miss_send_len: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'miss_send_len: ' + str(self.miss_send_len) + '\n'
    return outstr

class ofp_set_config (ofp_header):
  def __init__ (self, **kw):
    ofp_header.__init__(self)
    self.header_type = OFPT_SET_CONFIG
    self.flags = 0
    self.miss_send_len = OFP_DEFAULT_MISS_SEND_LEN
    self.length = 12

    initHelper(self, kw)

  def _assert (self):
    return (True, None)

  def pack (self, assertstruct=True):
    if(assertstruct):
      if(not self._assert()[0]):
        return None
    packed = ""
    packed += ofp_header.pack(self)
    packed += struct.pack("!HH", self.flags, self.miss_send_len)
    return packed

  def unpack (self, binaryString):
    if (len(binaryString) < 12):
      return binaryString
    ofp_header.unpack(self, binaryString[0:])
    (self.flags, self.miss_send_len) = struct.unpack_from("!HH", binaryString, 8)
    return binaryString[12:]

  def __len__ (self):
    return 12

  def __eq__ (self, other):
    if type(self) != type(other): return False
    if not ofp_header.__eq__(self, other): return False
    if self.flags !=  other.flags: return False
    if self.miss_send_len !=  other.miss_send_len: return False
    return True

  def __ne__ (self, other): return not self.__eq__(other)

  def show (self, prefix=''):
    outstr = ''
    outstr += prefix + 'header: \n'
    outstr += ofp_header.show(self, prefix + '  ')
    outstr += prefix + 'flags: ' + str(self.flags) + '\n'
    outstr += prefix + 'miss_send_len: ' + str(self.miss_send_len) + '\n'
    return outstr

ofp_port_rev_map = {
  'OFPP_MAX'        : 65280,
  'OFPP_IN_PORT'    : 65528,
  'OFPP_TABLE'      : 65529,
  'OFPP_NORMAL'     : 65530,
  'OFPP_FLOOD'      : 65531,
  'OFPP_ALL'        : 65532,
  'OFPP_CONTROLLER' : 65533,
  'OFPP_LOCAL'      : 65534,
  'OFPP_NONE'       : 65535,
}

ofp_type_rev_map = {
  'OFPT_HELLO'                    : 0,
  'OFPT_ERROR'                    : 1,
  'OFPT_ECHO_REQUEST'             : 2,
  'OFPT_ECHO_REPLY'               : 3,
  'OFPT_VENDOR'                   : 4,
  'OFPT_FEATURES_REQUEST'         : 5,
  'OFPT_FEATURES_REPLY'           : 6,
  'OFPT_GET_CONFIG_REQUEST'       : 7,
  'OFPT_GET_CONFIG_REPLY'         : 8,
  'OFPT_SET_CONFIG'               : 9,
  'OFPT_PACKET_IN'                : 10,
  'OFPT_FLOW_REMOVED'             : 11,
  'OFPT_PORT_STATUS'              : 12,
  'OFPT_PACKET_OUT'               : 13,
  'OFPT_FLOW_MOD'                 : 14,
  'OFPT_PORT_MOD'                 : 15,
  'OFPT_STATS_REQUEST'            : 16,
  'OFPT_STATS_REPLY'              : 17,
  'OFPT_BARRIER_REQUEST'          : 18,
  'OFPT_BARRIER_REPLY'            : 19,
  'OFPT_QUEUE_GET_CONFIG_REQUEST' : 20,
  'OFPT_QUEUE_GET_CONFIG_REPLY'   : 21,
}

# Table that maps an action type to a callable that creates that type
# (This is filled in by _init after the globals have been created)
_action_map = {}

def _unpack_actions (b, length, offset=0):
  """
  Parses actions from a buffer
  b is a buffer (bytes)
  offset, if specified is where in b to start decoding
  returns ([Actions], next_offset)
  """
  if (len(b) - offset) < length: return ([], offset)
  actions = []
  end = length + offset
  while offset < end:
    (t,l) = struct.unpack_from("!HH", b, offset)
    if (len(b) - offset) < l: return ([], offset)
    a = _action_map.get(t)
    if a is None:
      # Use generic action header for unknown type
      a = ofp_action_header()
    else:
      a = a()
    a.unpack(b[offset:offset+l])
    assert len(a) == l
    actions.append(a)
    offset += l
  return (actions, offset)

def _init ():
  def formatMap (name, m):
    o = name + " = {\n"
    vk = sorted([(v,k) for k,v in m.iteritems()])
    maxlen = 2 + len(reduce(lambda a,b: a if len(a)>len(b) else b, (v for k,v in vk)))
    fstr = "  %-" + str(maxlen) + "s : %s,\n"
    for v,k in vk:
      o += fstr % ("'" + k + "'",v)
    o += "}"
    return o
  """
  maps = []
  for k,v in globals().iteritems():
    if k.startswith("ofp_") and k.endswith("_map") and type(v) == dict:
      maps.append((k,v))
  for name,m in maps:
    rev = {}
    name = name[:-4]
    names = globals()[name]
    for n in names:
      rev[n] = globals()[n]

    globals()[name + '_rev_map'] = rev
    print formatMap(name + "_rev_map", rev)
  return
  """
  maps = []
  for k,v in globals().iteritems():
    if k.startswith("ofp_") and k.endswith("_rev_map") and type(v) == dict:
      maps.append((k[:-8],v))
  for name,m in maps:
    # Try to generate forward maps
    forward = dict(((v,k) for k,v in m.iteritems()))
    if len(forward) == len(m):
      globals()[name + "_map"] = forward
    else:
      print name + "_rev_map is not a map"

    # Try to generate lists
    v = m.values()
    v.sort()
    if v[-1] != len(v)-1:
      # Allow ones where the last value is a special value (e.g., VENDOR)
      del v[-1]
    if len(v) > 0 and v[0] == 0 and v[-1] == len(v)-1:
      globals()[name] = v

    # Generate gobals
    for k,v in m.iteritems():
      globals()[k] = v


_init()

# Fill in the action-to-class table
#TODO: Use the factory functions?
_action_map.update({
  #TODO: special type for OFPAT_STRIP_VLAN?
  OFPAT_OUTPUT                   : ofp_action_output,
  OFPAT_SET_VLAN_VID             : ofp_action_vlan_vid,
  OFPAT_SET_VLAN_PCP             : ofp_action_vlan_pcp,
  OFPAT_SET_DL_SRC               : ofp_action_dl_addr,
  OFPAT_SET_DL_DST               : ofp_action_dl_addr,
  OFPAT_SET_NW_SRC               : ofp_action_nw_addr,
  OFPAT_SET_NW_DST               : ofp_action_nw_addr,
  OFPAT_SET_NW_TOS               : ofp_action_nw_tos,
  OFPAT_SET_TP_SRC               : ofp_action_tp_port,
  OFPAT_SET_TP_DST               : ofp_action_tp_port,
  OFPAT_ENQUEUE                  : ofp_action_enqueue,
  OFPAT_PUSH_MPLS                : ofp_action_push_mpls,
  OFPAT_POP_MPLS                 : ofp_action_pop_mpls,
  OFPAT_SET_MPLS_LABEL           : ofp_action_mpls_label,
  OFPAT_SET_MPLS_TC              : ofp_action_mpls_tc,
  OFPAT_SET_MPLS_TTL             : ofp_action_mpls_ttl,
  OFPAT_DEC_MPLS_TTL             : ofp_action_mpls_dec_ttl,
  OFPAT_RESUBMIT                 : ofp_action_resubmit
  
})

# Values from macro definitions
OFP_FLOW_PERMANENT = 0
OFP_DL_TYPE_ETH2_CUTOFF = 0x0600
DESC_STR_LEN = 256
OFPFW_ICMP_CODE = OFPFW_TP_DST
OFPQ_MIN_RATE_UNCFG = 0xffff
OFP_VERSION = 0x01
OFP_MAX_TABLE_NAME_LEN = 32
OFP_DL_TYPE_NOT_ETH_TYPE = 0x05ff
OFP_DEFAULT_MISS_SEND_LEN = 128
OFP_MAX_PORT_NAME_LEN = 16
OFP_SSL_PORT = 6633
OFPFW_ICMP_TYPE = OFPFW_TP_SRC
OFP_TCP_PORT = 6633
SERIAL_NUM_LEN = 32
OFP_DEFAULT_PRIORITY = 0x8000
OFP_ETH_ALEN = 6
OFP_VLAN_NONE = 0xffff
OFPQ_ALL = 0xffffffff

# Basic structure size definitions.
OFP_ACTION_DL_ADDR_BYTES = 16
OFP_ACTION_ENQUEUE_BYTES = 16
OFP_ACTION_HEADER_BYTES = 8
OFP_ACTION_NW_ADDR_BYTES = 8
OFP_ACTION_NW_TOS_BYTES = 8
OFP_ACTION_OUTPUT_BYTES = 8
OFP_ACTION_TP_PORT_BYTES = 8
OFP_ACTION_VENDOR_HEADER_BYTES = 8
OFP_ACTION_VLAN_PCP_BYTES = 8
OFP_ACTION_VLAN_VID_BYTES = 8
OFP_AGGREGATE_STATS_REPLY_BYTES = 24
OFP_AGGREGATE_STATS_REQUEST_BYTES = 44
OFP_DESC_STATS_BYTES = 1056
OFP_ERROR_MSG_BYTES = 12
OFP_FLOW_MOD_BYTES = 72
OFP_FLOW_REMOVED_BYTES = 88
OFP_FLOW_STATS_BYTES = 88
OFP_FLOW_STATS_REQUEST_BYTES = 44
OFP_HEADER_BYTES = 8
OFP_HELLO_BYTES = 8
OFP_MATCH_BYTES = 40
OFP_PACKET_IN_BYTES = 18
OFP_PACKET_OUT_BYTES = 16
OFP_PACKET_QUEUE_BYTES = 8
OFP_PHY_PORT_BYTES = 48
OFP_PORT_MOD_BYTES = 32
OFP_PORT_STATS_BYTES = 104
OFP_PORT_STATS_REQUEST_BYTES = 8
OFP_PORT_STATUS_BYTES = 64
OFP_QUEUE_GET_CONFIG_REPLY_BYTES = 16
OFP_QUEUE_GET_CONFIG_REQUEST_BYTES = 12
OFP_QUEUE_PROP_HEADER_BYTES = 8
OFP_QUEUE_PROP_MIN_RATE_BYTES = 16
OFP_QUEUE_STATS_BYTES = 32
OFP_QUEUE_STATS_REQUEST_BYTES = 8
OFP_STATS_REPLY_BYTES = 12
OFP_STATS_REQUEST_BYTES = 12
OFP_SWITCH_CONFIG_BYTES = 12
OFP_SWITCH_FEATURES_BYTES = 32
OFP_TABLE_STATS_BYTES = 64
OFP_VENDOR_HEADER_BYTES = 12

NO_BUFFER = 4294967295

ofp_match_data = {
#  'wildcards' : (0, 0),
  'in_port' : (0, OFPFW_IN_PORT),
  'dl_src' : (EMPTY_ETH, OFPFW_DL_SRC),
  'dl_dst' : (EMPTY_ETH, OFPFW_DL_DST),
  'dl_vlan' : (0, OFPFW_DL_VLAN),
  'dl_vlan_pcp' : (0, OFPFW_DL_VLAN_PCP),
  #'pad1' : (_PAD, 0),
  'dl_type' : (0, OFPFW_DL_TYPE),
  'nw_tos' : (0, OFPFW_NW_TOS),
  'nw_proto' : (0, OFPFW_NW_PROTO),
  #'pad2' : (_PAD2, 0),
  'nw_src' : (0, OFPFW_NW_SRC_ALL),
  'nw_dst' : (0, OFPFW_NW_DST_ALL),
  'tp_src' : (0, OFPFW_TP_SRC),
  'tp_dst' : (0, OFPFW_TP_DST),
#  'mpls_label': (0, OFPFW_MPLS_LABEL),
#  'mpls_tc': (0, OFPFW_MPLS_TC),
}
